﻿// <copyright file="PolylineVisual.cs" company="Oleg V. Polikarpotchkin">
// Copyright © 2008 Oleg V. Polikarpotchkin. All Right Reserved
// </copyright>
// <author>Oleg V. Polikarpotchkin</author>
// <email>ov-p@yandex.ru</email>
// <date>2008-12-23</date>
// <summary>OpenWPFChart library. Polyline Sampled Curve visual draws the curve passed in by Points data as polyline.</summary>
// <revision>$Id$</revision>

using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;

namespace OpenWPFChart.Parts
{
	/// <summary>
	/// Polyline Sampled Curve visual class draws the curve passed in by Points data
	/// as polyline.
	/// </summary>
	/// <remarks>
	/// Two adjacent points are connected by the straight line.
	/// <para>If PointMarkerGeometry property is set the data point markers are drawn.</para>
	/// </remarks>
	[Interpolated("Polyline")]
	public class PolylineVisual : ItemVisual, ICurveVisual
	{
		/// <summary>
		/// Renders the Curve
		/// </summary>
		protected internal override void Render()
		{
			Children.Clear();

			using (DrawingContext dc = RenderOpen())
			{
				SampledCurveDataView dataView = DataView as SampledCurveDataView;
				if (dataView == null)
					return;
				IPoint2DSeries pointSeries = dataView.ItemData as IPoint2DSeries;
				if (pointSeries == null)
					return;

				ChartScale hScale = dataView.HorizontalScale, vScale = dataView.VerticalScale;
				if (hScale == null || !hScale.CompatibleWith(dataView.ItemData.GetDimensionType(0))
					|| vScale == null || !vScale.CompatibleWith(dataView.ItemData.GetDimensionType(1)))
					return;

				// Chart area size.
				Size areaSize;
				if (dataView.Orientation == Orientation.Horizontal)
					areaSize = new Size(hScale.ToPixels(hScale.Stop), vScale.ToPixels(vScale.Stop));
				else
					areaSize = new Size(vScale.ToPixels(vScale.Stop), hScale.ToPixels(hScale.Stop));

				// Clipping region.
				RectangleGeometry clip = new RectangleGeometry(new Rect(0, 0
						, areaSize.Width, areaSize.Height));
				dc.PushClip(clip);

				// Curve point marker drawing.
				IPointMarker iPointMarker = dataView as IPointMarker;
				Debug.Assert(iPointMarker != null, "iPointMarker != null");
				Drawing pointMarkerDrawing = null;
				if (iPointMarker.PointMarkerVisible)
					pointMarkerDrawing = iPointMarker.PointMarker;

				// Curve points
				Point? startPoint = null;
				List<Point> linePoints = new List<Point>();
				foreach (var pt in pointSeries.Points)
				{
					double x, y;
					if (dataView.Orientation == Orientation.Horizontal)
					{
						x = hScale.ToPixels(pt.X);
						y = vScale.ToPixels(pt.Y);
					}
					else // Orientation == Orientation.Vertical
					{
						y = hScale.ToPixels(pt.X);
						x = vScale.ToPixels(pt.Y);
					}

					if (!startPoint.HasValue)
						startPoint = new Point(x, y);
					else
						linePoints.Add(new Point(x, y));

					if (pointMarkerDrawing != null && isInsideArea(new Point(x, y), areaSize))
					{ // Curve point marker.
						Drawing marker = pointMarkerDrawing.Clone();
						marker.Freeze();

						PointVisual pointMarker = new PointVisual(marker);
						pointMarker.Transform = new TranslateTransform(x, y);
						Children.Add(pointMarker);
					}
				}
				if (!startPoint.HasValue)
					return; // Nothing to draw.

				if (dataView.FillArea && dataView.AreaBrush != null)
				{ // Draw the Curve Area.
					// Curve Area figure.
					PathFigure figure = new PathFigure(startPoint.Value
						, new PathSegment[] { new PolyLineSegment(linePoints, false) }, true);
					// Make the polygon.
					if (dataView.Orientation == Orientation.Horizontal)
					{
						Point bottomRight = new Point(linePoints[linePoints.Count - 1].X
							, areaSize.Height);
						Point bottomLeft = new Point(startPoint.Value.X, areaSize.Height);
						
						figure.Segments.Add(new LineSegment(bottomRight, false));
						figure.Segments.Add(new LineSegment(bottomLeft, false));
					}
					else // dataView.Orientation == Orientation.Vertical
					{
						Point bottomLeft = new Point(0, linePoints[linePoints.Count - 1].Y);
						Point topLeft = new Point(0, startPoint.Value.Y);

						figure.Segments.Add(new LineSegment(bottomLeft, false));
						figure.Segments.Add(new LineSegment(topLeft, false));
					}

					// Curve Area geometry.
					PathGeometry geometry = new PathGeometry(new PathFigure[] { figure });
					geometry.Freeze();

					dc.DrawGeometry(dataView.AreaBrush, null, geometry);
				}

				if (dataView.Pen != null)
				{ // Draw the Curve.
					// Curve geometry.
					StreamGeometry geometry = new StreamGeometry();
					using (StreamGeometryContext ctx = geometry.Open())
					{
						ctx.BeginFigure(startPoint.Value, false /* is filled */, false /* is closed */);
						ctx.PolyLineTo(linePoints, true /* is stroked */, true /* is smooth join */);
					}
					geometry.Freeze();

					dc.DrawGeometry(Brushes.Transparent, dataView.Pen, geometry);
				}
			}
		}

		#region ICurveVisual Members
		/// <inheritdoc />
		public CurvePath GetCurvePath(bool isStroked)
		{
			SampledCurveDataView dataView = DataView as SampledCurveDataView;
			if (dataView == null)
				return new CurvePath();
			IPoint2DSeries pointSeries = dataView.ItemData as IPoint2DSeries;
			if (pointSeries == null)
				return new CurvePath();

			ChartScale hScale = dataView.HorizontalScale, vScale = dataView.VerticalScale;
			if (hScale == null || !hScale.CompatibleWith(dataView.ItemData.GetDimensionType(0))
				|| vScale == null || !vScale.CompatibleWith(dataView.ItemData.GetDimensionType(1)))
				return new CurvePath();

			// Curve points
			Point? startPoint = null;
			List<Point> linePoints = new List<Point>();
			foreach (var pt in pointSeries.Points)
			{
				double x, y;
				if (dataView.Orientation == Orientation.Horizontal)
				{
					x = hScale.ToPixels(pt.X);
					y = vScale.ToPixels(pt.Y);
				}
				else // Orientation == Orientation.Vertical
				{
					y = hScale.ToPixels(pt.X);
					x = vScale.ToPixels(pt.Y);
				}

				if (!startPoint.HasValue)
					startPoint = new Point(x, y);
				else
					linePoints.Add(new Point(x, y));
			}
			if (!startPoint.HasValue)
				return new CurvePath(); // Empty Path.

			return new CurvePath(new PathSegment[] { new PolyLineSegment(linePoints, isStroked) }
				, startPoint.Value, linePoints[linePoints.Count - 1]);
		}
		#endregion ICurveVisual Members
	}
}
